{***************************************************************************}
{                                                                           }
{           Spring Framework for Delphi                                     }
{                                                                           }
{           Copyright (C) 2009-2011 DevJET                                  }
{                                                                           }
{           http://www.spring4d.org                                         }
{                                                                           }
{***************************************************************************}
{                                                                           }
{  Licensed under the Apache License, Version 2.0 (the "License");          }
{  you may not use this file except in compliance with the License.         }
{  You may obtain a copy of the License at                                  }
{                                                                           }
{      http://www.apache.org/licenses/LICENSE-2.0                           }
{                                                                           }
{  Unless required by applicable law or agreed to in writing, software      }
{  distributed under the License is distributed on an "AS IS" BASIS,        }
{  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
{  See the License for the specific language governing permissions and      }
{  limitations under the License.                                           }
{                                                                           }
{***************************************************************************}

unit Spring.Serialization.Base;

{$I Spring.inc}

interface

uses
  Rtti,
  TypInfo,
  Spring,
  Spring.Serialization,
  Spring.Serialization.Formatter,
  Spring.Reflection,
  Spring.Reflection.ValueExpression,
  Spring.Collections;

type

  {$REGION 'Documentation'}
  ///	<summary>Base abstract implementation of <see cref=
  ///	"Spring.Serialization.IReferenceProvider" />.</summary>
  ///	<remarks>Provides common logic of reference providers.</remarks>
  {$ENDREGION}
  TReferenceProviderBase = class abstract(TValueProviderBase,
    IReferenceProvider, IValueProvider)
  private
    fReflector: IReflection;
    function GetParent: IReferenceProvider;
    function GetReflector: IReflection;
  protected
    fParent: IReferenceProvider;

    {$REGION 'Property Accessors'}
      function GetReferenceType: TRttiType; virtual; abstract;
      function GetName: string; virtual; abstract;
      function GetAttributes: TArray<TCustomAttribute>; virtual; abstract;
      function GetIsWriteOnly: Boolean; virtual; abstract;
      function GetIgnoreSerialization: Boolean; virtual;
      function GetVisibility: TMemberVisibility; virtual;
    {$ENDREGION}
    property Reflector: IReflection read GetReflector;
  public
    property Name: string read GetName;
    property ReferenceType: TRttiType read GetReferenceType;
    property Attributes: TArray<TCustomAttribute> read GetAttributes;
    property IgnoreSerialization: Boolean read GetIgnoreSerialization;
    property Visibility: TMemberVisibility read GetVisibility;
    property Parent: IReferenceProvider read GetParent;
    property IsWriteOnly: Boolean read GetIsWriteOnly;
  end;

  {$REGION 'Documentation'}
  ///	<summary>
  ///	  <para>It defines ancestor class represents members of structure
  ///	  reference types such as class, interface and records.</para>
  ///	  <para>Base abstract class which provide common behaviour in some
  ///	  generic method of <see cref=
  ///	  "Spring.Serialization.IReferenceProvider">IReferenceProvider</see>
  ///	  interface.</para>
  ///	</summary>
  {$ENDREGION}
  TMemberReference = class abstract(TReferenceProviderBase)
  private
    fInstancesMap: IList<TObject>;
    function GetInstancesMap: IList<TObject>;
  protected
    fInstance: TValue;
    fMember: TRttiMember;
    function GetName: string; override;
    function GetAttributes: TArray<TCustomAttribute>; override;
    function GetVisibility: TMemberVisibility; override;
    property InstancesMap: IList<TObject> read GetInstancesMap;
  public
    constructor Create(const instance: TValue;
      const member: TRttiMember;
      const parent: IReferenceProvider);

    property Name: string read GetName;
    property Attributes: TArray<TCustomAttribute> read GetAttributes;
  end;

  TTypeSerializerBase = class abstract(TInterfacedObject, ITypeSerializer, IInterface)
  private
    fSerializeCallback: TSerializationCallback;
    fDeserializeCallback: TSerializationCallback;

    {$REGION 'Property Accessors'}
      function GetSerializeCallback: TSerializationCallback;
      procedure SetSerializeCallback(const Value: TSerializationCallback);
      function GetDeserializeCallback: TSerializationCallback;
      procedure SetDeserializeCallback(const Value: TSerializationCallback);
    {$ENDREGION}
  protected
    procedure DoDeserializeCallback(const instance: IReferenceProvider;
      const parentNode: IContentNode);
    procedure DoSerializeCallback(const instance: IReferenceProvider;
      const parentNode: IContentNode);
  public
    function Accept(const instance: IReferenceProvider;
      operation: TSerializerOperation): Boolean; virtual; abstract;
    function Final(const instance: IReferenceProvider): Boolean; virtual; abstract;
    procedure Deserialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); virtual; abstract;
    procedure Serialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); virtual; abstract;
    property SerializeCallback: TSerializationCallback read GetSerializeCallback
      write SetSerializeCallback;
  end;

implementation

{ TReferenceProviderBase }

function TReferenceProviderBase.GetParent: IReferenceProvider;
begin
  Result := fParent;
end;

function TReferenceProviderBase.GetVisibility: TMemberVisibility;
begin
  Result := mvPublic;
end;

function TReferenceProviderBase.GetIgnoreSerialization: Boolean;
var
  attribute: TCustomAttribute;
begin
  Result := false;
  for attribute in Attributes do
  begin
    if attribute.ClassType = Spring.Serialization.IgnoreSerialization then
    begin
      Result := True;
      Break;
    end;
  end;
end;

function TReferenceProviderBase.GetReflector: IReflection;
begin
  if not Assigned(fReflector) then
    fReflector := TReflection.Create;

  Result := fReflector;
end;


{$REGION 'TMemberReference'}

constructor TMemberReference.Create(const instance: TValue;
  const member: TRttiMember; const parent: IReferenceProvider);
begin
  inherited Create;

  TArgument.CheckNotNull(instance, 'instance');
  TArgument.CheckNotNull(member, 'member');

  fParent := parent;
  fInstance := instance;
  fMember := member;

  if ReferenceType.IsInstance or
    fInstance.IsObject then
  begin
    if not InstancesMap.Contains(instance.AsObject) then
      InstancesMap.Add(instance.AsObject)
    else
    begin
      fInstance := TValue.Empty;
      fMember := nil;
    end;
  end;
end;

function TMemberReference.GetAttributes: TArray<TCustomAttribute>;
begin
  if Assigned(fMember) then
    Result := fMember.GetAttributes;
end;

function TMemberReference.GetInstancesMap: IList<TObject>;
begin
  if Assigned(fParent) and
    (fParent is TMemberReference) then
  begin
    Result := (fParent as TMemberReference).InstancesMap
  end
  else
  begin
    if not Assigned(fInstancesMap) then
      fInstancesMap := Spring.Collections.TCollections.CreateList<TObject>;

    Result := fInstancesMap;
  end;
end;

function TMemberReference.GetName: string;
var
  rttiType: TRttiType;
begin
  if Assigned(fMember) then
    Result := fMember.Name
  else
    begin
      rttiType := Reflector.GetType(fInstance.TypeInfo);
      if Assigned(rttiType) then
        Result := rttiType.QualifiedName
      else
        Result := 'nil';
    end;
end;

function TMemberReference.GetVisibility: TMemberVisibility;
begin
  if Assigned(fMember) then
    Result := fMember.Visibility
  else
    Result := inherited;
end;

{$ENDREGION}


{$REGION 'TTypeSerializerBase'}

procedure TTypeSerializerBase.DoDeserializeCallback(
  const instance: IReferenceProvider; const parentNode: IContentNode);
begin
  if Assigned(fDeserializeCallback) then
    fDeserializeCallback(instance, parentNode);
end;

procedure TTypeSerializerBase.DoSerializeCallback(const instance: IReferenceProvider;
  const parentNode: IContentNode);
begin
  if Assigned(fSerializeCallback) then
    fSerializeCallback(instance, parentNode);
end;

function TTypeSerializerBase.GetDeserializeCallback: TSerializationCallback;
begin
  Result := fDeserializeCallback;
end;

function TTypeSerializerBase.GetSerializeCallback: TSerializationCallback;
begin
  Result := fSerializeCallback;
end;

procedure TTypeSerializerBase.SetDeserializeCallback(
  const Value: TSerializationCallback);
begin
  fDeserializeCallback := Value;
end;

procedure TTypeSerializerBase.SetSerializeCallback(const Value: TSerializationCallback);
begin
  fSerializeCallback := Value;
end;

{$ENDREGION}


end.
